Skip to content

AMF remoting and runtime fixes - #25

Open
neilrackett wants to merge 5 commits into
awayfl:devfrom
mesmotronic:feat/amf-remoting-and-runtime-fixes
Open

AMF remoting and runtime fixes#25
neilrackett wants to merge 5 commits into
awayfl:devfrom
mesmotronic:feat/amf-remoting-and-runtime-fixes

Conversation

@neilrackett

Copy link
Copy Markdown

Flash Remoting (AMF over HTTP) support + related runtime fixes

This PR makes classic AMF remoting apps work under AwayFL: apps built on
NetConnection.connect(gatewayUrl) + call('Service.method', responder, ...args)
against Zend_Amf / AMFPHP / FluorineFx-style PHP/.NET gateways.

It was developed by reviving a real-world AS3/Flex-era business app (a
Proxy-based AMF service layer talking to a Zend_Amf backend) and contains one
feature plus four small fixes that the same app surfaced along the way. Happy
to split any of these into separate PRs if preferred — each is an independent
commit touching disjoint files.

Commits

  1. NetConnection: implement Flash Remoting (AMF over HTTP POST)
    connect()/call() were stubs. Now: serialisation through the AVM2
    ByteArray AMF codec (arguments and results stay proper AVM2 objects), a
    hand-written remoting packet envelope, fetch POST with credentials,
    fault routing to Responder.status / NetConnection.Call.Failed, and
    opt-in wire logging via self.__AWAYFL_AMF_DEBUG = true.
    Notable implementation details (each learned the hard way):

    • ByteArray/NetStatusEvent are constructed through their AXClass —
      raw new produces instances without runtime traits (ByteArray's buffer
      machinery lives in DataBuffer instanceNatives; event property access
      from compiled AS3 needs the VM-linked prototype).
    • Native methods receive AS3 arguments flat (no ...rest collection), so
      call arguments are collected from arguments.
    • rtmp/rtmpt/rtmps still dispatch Connect.Failed (unchanged).
  2. EventDispatcherBase: report the aggregation target as currentTarget
    Flash reports the EventDispatcher(target) aggregation target as
    event.currentTarget. GreenSock v12 relies on this
    (event.currentTarget as TweenMax returned null and handlers crashed).

  3. DisplayObjectContainer: don't throw #2025 when the adaptee is already
    detached
    — the internal scene graph can diverge from the AS3 display
    list; the throw could erupt through a tween onComplete and kill the
    shared ticker (app frozen: rendering continues, no events/input).

  4. Link flash.ui.MouseCursorData — content constructing it for
    Mouse.registerCursor died with "Class native is not defined", a hard VM
    error, even though registerCursor itself is a no-op. Fresh data-holder
    implementation (the _shumway_flash one has stale imports).

  5. FileReference: satisfy current @types/wicg-file-system-access
    (build fix; extensions must be typed `.${string}`).

Verification

Tested end-to-end against a Zend_Amf gateway with the aforementioned app:
boot, login flow (authUser/authToken), data list calls, and both fault
paths (unknown method / unknown service), plus browser-automation regression
runs of the full boot→login→data flow.

Companion PR in avm2 (independent, but the same app needs both):
Proxy trap dispatch fix for public-mangled flash_proxy overrides.

connect() and call() were stubs (somewhatImplemented), so classic
AMF remoting apps - NetConnection.connect(gatewayUrl) +
call('Service.method', responder, ...args) against Zend_Amf / AMFPHP /
FluorineFx-style gateways - could not talk to their backend at all.

- connect(): a null command or non-rtmp url is a remoting gateway
  connection; it succeeds immediately (calls are independent HTTP POSTs)
  and dispatches NetConnection.Connect.Success asynchronously so listeners
  registered right after connect() still receive it. rtmp/rtmpt/rtmps
  remain unimplemented and dispatch Connect.Failed.
- call(): serialises arguments with the AVM2 ByteArray AMF codec (AMF3
  body behind the 0x11 avmplus marker in an AMF0 envelope), wraps them in
  a hand-written remoting packet, POSTs application/x-amf via fetch with
  credentials included, parses the response envelope, decodes bodies back
  through the same codec, and routes /<id>/onResult|onStatus to the
  matching Responder. Native call arguments arrive flat (no rest
  collection), so everything after the responder argument is captured
  via arguments.
- Faults: HTTP errors, non-AMF responses (misconfigured gateways answering
  200 with an HTML error page) and synchronous failures are routed to the
  Responder's status callback, falling back to a
  NetConnection.Call.Failed netStatus event.
- Implementation notes: ByteArray must be constructed through its AXClass
  (its buffer machinery comes from DataBuffer instanceNatives and only
  exists on the VM-linked prototype); NetStatusEvent likewise, so property
  access from compiled AS3 sees proper runtime traits; .sec is set on the
  ByteArray for the AMF codec (same idiom as SharedObject).
- Responder: add invokeResult/invokeStatus helpers encapsulating the
  callback invocation.
- Opt-in wire logging via self.__AWAYFL_AMF_DEBUG = true.

Verified end-to-end against a Zend_Amf gateway (login flow, list calls,
fault paths) with an AS3 app built on a Proxy-based AMF service layer.
When an EventDispatcher is constructed with a target argument
(the IEventDispatcher aggregation pattern), Flash reports that target -
not the internal dispatcher - as event.currentTarget during dispatch.

GreenSock (TweenLite/TweenMax v12) dispatches its tween events through an
aggregated EventDispatcher, and typical handler code does

    var tween:TweenMax = event.currentTarget as TweenMax;
    tween.removeEventListener(event.type, handler);

Without this fix the `as` cast returns null and such handlers crash
(and, being inside the tween render pass, can take the frame loop's
callback chain down with them).

target was already reported through _t; do the same for currentTarget.
…y detached

The internal (awayjs) scene graph can diverge from the AS3 display list
via orphan/unload management, so removeChild() on a child that is still
in the AS3 display list may find the adaptee already detached. Throwing
ArgumentError #2025 in that situation erupts through whatever frame
callback is currently running - e.g. a tween's onComplete removing a
preloader - and can kill the shared ticker, freezing the entire app
(rendering continues but no frame events and no input are processed).

Warn and treat the child as removed instead. This intentionally deviates
from Flash for the divergence case; genuinely removing a never-added
child now warns rather than throws, which is the safer failure mode here.
Content that uses custom cursors constructs MouseCursorData before
passing it to Mouse.registerCursor(); with the class unlinked, class
initialization aborts with 'Class native is not defined:
flash.ui.MouseCursorData' - a hard VM error that prevents the SWF's
script from running at all, even though registerCursor itself is a
harmless no-op here.

Link a fresh, working data holder (data/hotSpot/frameRate). The
_shumway_flash implementation is not used: it has stale relative imports
and notImplemented accessors.
Recent versions of the typings require file extensions in
SaveFilePickerOptions accept maps to be typed as `.${string}`;
the plain string no longer compiles.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant